1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 package java.text;
40
41 import java.io.IOException;
42 import java.io.InvalidObjectException;
43 import java.io.ObjectInputStream;
44 import java.util.Calendar;
45 import java.util.Date;
46 import java.util.GregorianCalendar;
47 import java.util.Locale;
48 import java.util.Map;
49 import java.util.MissingResourceException;
50 import java.util.ResourceBundle;
51 import java.util.SimpleTimeZone;
52 import java.util.TimeZone;
53 import java.util.concurrent.ConcurrentHashMap;
54 import java.util.concurrent.ConcurrentMap;
55 import sun.util.calendar.CalendarUtils;
56 import sun.util.calendar.ZoneInfoFile;
57 import sun.util.resources.LocaleData;
58
59 import static java.text.DateFormatSymbols.*;
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409 public class SimpleDateFormat extends DateFormat {
410
411
412
413 static final long serialVersionUID = 4774881970558875024L;
414
415
416
417
418 static final int currentSerialVersion = 1;
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434 private int serialVersionOnStream = currentSerialVersion;
435
436
437
438
439
440
441 private String pattern;
442
443
444
445
446
447 transient private NumberFormat originalNumberFormat;
448 transient private String originalNumberPattern;
449
450
451
452
453 transient private char minusSign = '-';
454
455
456
457
458
459 transient private boolean hasFollowingMinusSign = false;
460
461
462
463
464 transient private char[] compiledPattern;
465
466
467
468
469 private final static int TAG_QUOTE_ASCII_CHAR = 100;
470 private final static int TAG_QUOTE_CHARS = 101;
471
472
473
474
475
476
477 transient private char zeroDigit;
478
479
480
481
482
483
484
485 private DateFormatSymbols formatData;
486
487
488
489
490
491
492
493
494 private Date defaultCenturyStart;
495
496 transient private int defaultCenturyStartYear;
497
498 private static final int MILLIS_PER_MINUTE = 60 * 1000;
499
500
501
502 private static final String GMT = "GMT";
503
504
505
506
507 private static final ConcurrentMap<Locale, String[]> cachedLocaleData
508 = new ConcurrentHashMap<Locale, String[]>(3);
509
510
511
512
513 private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData
514 = new ConcurrentHashMap<Locale, NumberFormat>(3);
515
516
517
518
519
520
521
522
523
524
525 private Locale locale;
526
527
528
529
530
531
532
533
534 transient boolean useDateFormatSymbols;
535
536
537
538
539
540
541
542
543 public SimpleDateFormat() {
544 this(SHORT, SHORT, Locale.getDefault(Locale.Category.FORMAT));
545 }
546
547
548
549
550
551
552
553
554
555
556
557
558 public SimpleDateFormat(String pattern)
559 {
560 this(pattern, Locale.getDefault(Locale.Category.FORMAT));
561 }
562
563
564
565
566
567
568
569
570
571
572
573
574
575 public SimpleDateFormat(String pattern, Locale locale)
576 {
577 if (pattern == null || locale == null) {
578 throw new NullPointerException();
579 }
580
581 initializeCalendar(locale);
582 this.pattern = pattern;
583 this.formatData = DateFormatSymbols.getInstanceRef(locale);
584 this.locale = locale;
585 initialize(locale);
586 }
587
588
589
590
591
592
593
594
595
596
597 public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols)
598 {
599 if (pattern == null || formatSymbols == null) {
600 throw new NullPointerException();
601 }
602
603 this.pattern = pattern;
604 this.formatData = (DateFormatSymbols) formatSymbols.clone();
605 this.locale = Locale.getDefault(Locale.Category.FORMAT);
606 initializeCalendar(this.locale);
607 initialize(this.locale);
608 useDateFormatSymbols = true;
609 }
610
611
612 SimpleDateFormat(int timeStyle, int dateStyle, Locale loc) {
613 if (loc == null) {
614 throw new NullPointerException();
615 }
616
617 this.locale = loc;
618
619 initializeCalendar(loc);
620
621
622 String[] dateTimePatterns = cachedLocaleData.get(loc);
623 if (dateTimePatterns == null) {
624 ResourceBundle r = LocaleData.getDateFormatData(loc);
625 if (!isGregorianCalendar()) {
626 try {
627 dateTimePatterns = r.getStringArray(getCalendarName() + ".DateTimePatterns");
628 } catch (MissingResourceException e) {
629 }
630 }
631 if (dateTimePatterns == null) {
632 dateTimePatterns = r.getStringArray("DateTimePatterns");
633 }
634
635 cachedLocaleData.putIfAbsent(loc, dateTimePatterns);
636 }
637 formatData = DateFormatSymbols.getInstanceRef(loc);
638 if ((timeStyle >= 0) && (dateStyle >= 0)) {
639 Object[] dateTimeArgs = {dateTimePatterns[timeStyle],
640 dateTimePatterns[dateStyle + 4]};
641 pattern = MessageFormat.format(dateTimePatterns[8], dateTimeArgs);
642 }
643 else if (timeStyle >= 0) {
644 pattern = dateTimePatterns[timeStyle];
645 }
646 else if (dateStyle >= 0) {
647 pattern = dateTimePatterns[dateStyle + 4];
648 }
649 else {
650 throw new IllegalArgumentException("No date or time style specified");
651 }
652
653 initialize(loc);
654 }
655
656
657 private void initialize(Locale loc) {
658
659 compiledPattern = compile(pattern);
660
661
662 numberFormat = cachedNumberFormatData.get(loc);
663 if (numberFormat == null) {
664 numberFormat = NumberFormat.getIntegerInstance(loc);
665 numberFormat.setGroupingUsed(false);
666
667
668 cachedNumberFormatData.putIfAbsent(loc, numberFormat);
669 }
670 numberFormat = (NumberFormat) numberFormat.clone();
671
672 initializeDefaultCentury();
673 }
674
675 private void initializeCalendar(Locale loc) {
676 if (calendar == null) {
677 assert loc != null;
678
679
680
681
682 calendar = Calendar.getInstance(TimeZone.getDefault(), loc);
683 }
684 }
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750 private char[] compile(String pattern) {
751 int length = pattern.length();
752 boolean inQuote = false;
753 StringBuilder compiledPattern = new StringBuilder(length * 2);
754 StringBuilder tmpBuffer = null;
755 int count = 0;
756 int lastTag = -1;
757
758 for (int i = 0; i < length; i++) {
759 char c = pattern.charAt(i);
760
761 if (c == '\'') {
762
763
764 if ((i + 1) < length) {
765 c = pattern.charAt(i + 1);
766 if (c == '\'') {
767 i++;
768 if (count != 0) {
769 encode(lastTag, count, compiledPattern);
770 lastTag = -1;
771 count = 0;
772 }
773 if (inQuote) {
774 tmpBuffer.append(c);
775 } else {
776 compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
777 }
778 continue;
779 }
780 }
781 if (!inQuote) {
782 if (count != 0) {
783 encode(lastTag, count, compiledPattern);
784 lastTag = -1;
785 count = 0;
786 }
787 if (tmpBuffer == null) {
788 tmpBuffer = new StringBuilder(length);
789 } else {
790 tmpBuffer.setLength(0);
791 }
792 inQuote = true;
793 } else {
794 int len = tmpBuffer.length();
795 if (len == 1) {
796 char ch = tmpBuffer.charAt(0);
797 if (ch < 128) {
798 compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | ch));
799 } else {
800 compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | 1));
801 compiledPattern.append(ch);
802 }
803 } else {
804 encode(TAG_QUOTE_CHARS, len, compiledPattern);
805 compiledPattern.append(tmpBuffer);
806 }
807 inQuote = false;
808 }
809 continue;
810 }
811 if (inQuote) {
812 tmpBuffer.append(c);
813 continue;
814 }
815 if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
816 if (count != 0) {
817 encode(lastTag, count, compiledPattern);
818 lastTag = -1;
819 count = 0;
820 }
821 if (c < 128) {
822
823 compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
824 } else {
825
826
827 int j;
828 for (j = i + 1; j < length; j++) {
829 char d = pattern.charAt(j);
830 if (d == '\'' || (d >= 'a' && d <= 'z' || d >= 'A' && d <= 'Z')) {
831 break;
832 }
833 }
834 compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | (j - i)));
835 for (; i < j; i++) {
836 compiledPattern.append(pattern.charAt(i));
837 }
838 i--;
839 }
840 continue;
841 }
842
843 int tag;
844 if ((tag = DateFormatSymbols.patternChars.indexOf(c)) == -1) {
845 throw new IllegalArgumentException("Illegal pattern character " +
846 "'" + c + "'");
847 }
848 if (lastTag == -1 || lastTag == tag) {
849 lastTag = tag;
850 count++;
851 continue;
852 }
853 encode(lastTag, count, compiledPattern);
854 lastTag = tag;
855 count = 1;
856 }
857
858 if (inQuote) {
859 throw new IllegalArgumentException("Unterminated quote");
860 }
861
862 if (count != 0) {
863 encode(lastTag, count, compiledPattern);
864 }
865
866
867 int len = compiledPattern.length();
868 char[] r = new char[len];
869 compiledPattern.getChars(0, len, r, 0);
870 return r;
871 }
872
873
874
875
876 private static final void encode(int tag, int length, StringBuilder buffer) {
877 if (tag == PATTERN_ISO_ZONE && length >= 4) {
878 throw new IllegalArgumentException("invalid ISO 8601 format: length=" + length);
879 }
880 if (length < 255) {
881 buffer.append((char)(tag << 8 | length));
882 } else {
883 buffer.append((char)((tag << 8) | 0xff));
884 buffer.append((char)(length >>> 16));
885 buffer.append((char)(length & 0xffff));
886 }
887 }
888
889
890
891
892 private void initializeDefaultCentury() {
893 calendar.setTimeInMillis(System.currentTimeMillis());
894 calendar.add( Calendar.YEAR, -80 );
895 parseAmbiguousDatesAsAfter(calendar.getTime());
896 }
897
898
899
900
901 private void parseAmbiguousDatesAsAfter(Date startDate) {
902 defaultCenturyStart = startDate;
903 calendar.setTime(startDate);
904 defaultCenturyStartYear = calendar.get(Calendar.YEAR);
905 }
906
907
908
909
910
911
912
913
914
915
916 public void set2DigitYearStart(Date startDate) {
917 parseAmbiguousDatesAsAfter(new Date(startDate.getTime()));
918 }
919
920
921
922
923
924
925
926
927
928
929 public Date get2DigitYearStart() {
930 return (Date) defaultCenturyStart.clone();
931 }
932
933
934
935
936
937
938
939
940
941
942
943
944 public StringBuffer format(Date date, StringBuffer toAppendTo,
945 FieldPosition pos)
946 {
947 pos.beginIndex = pos.endIndex = 0;
948 return format(date, toAppendTo, pos.getFieldDelegate());
949 }
950
951
952 private StringBuffer format(Date date, StringBuffer toAppendTo,
953 FieldDelegate delegate) {
954
955 calendar.setTime(date);
956
957 boolean useDateFormatSymbols = useDateFormatSymbols();
958
959 for (int i = 0; i < compiledPattern.length; ) {
960 int tag = compiledPattern[i] >>> 8;
961 int count = compiledPattern[i++] & 0xff;
962 if (count == 255) {
963 count = compiledPattern[i++] << 16;
964 count |= compiledPattern[i++];
965 }
966
967 switch (tag) {
968 case TAG_QUOTE_ASCII_CHAR:
969 toAppendTo.append((char)count);
970 break;
971
972 case TAG_QUOTE_CHARS:
973 toAppendTo.append(compiledPattern, i, count);
974 i += count;
975 break;
976
977 default:
978 subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
979 break;
980 }
981 }
982 return toAppendTo;
983 }
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002 public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
1003 StringBuffer sb = new StringBuffer();
1004 CharacterIteratorFieldDelegate delegate = new
1005 CharacterIteratorFieldDelegate();
1006
1007 if (obj instanceof Date) {
1008 format((Date)obj, sb, delegate);
1009 }
1010 else if (obj instanceof Number) {
1011 format(new Date(((Number)obj).longValue()), sb, delegate);
1012 }
1013 else if (obj == null) {
1014 throw new NullPointerException(
1015 "formatToCharacterIterator must be passed non-null object");
1016 }
1017 else {
1018 throw new IllegalArgumentException(
1019 "Cannot format given Object as a Date");
1020 }
1021 return delegate.getIterator(sb.toString());
1022 }
1023
1024
1025 private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD =
1026 {
1027 Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.DATE,
1028 Calendar.HOUR_OF_DAY, Calendar.HOUR_OF_DAY, Calendar.MINUTE,
1029 Calendar.SECOND, Calendar.MILLISECOND, Calendar.DAY_OF_WEEK,
1030 Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH,
1031 Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH,
1032 Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET,
1033 Calendar.ZONE_OFFSET,
1034
1035 CalendarBuilder.WEEK_YEAR,
1036 CalendarBuilder.ISO_DAY_OF_WEEK,
1037 Calendar.ZONE_OFFSET
1038 };
1039
1040
1041 private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = {
1042 DateFormat.ERA_FIELD, DateFormat.YEAR_FIELD, DateFormat.MONTH_FIELD,
1043 DateFormat.DATE_FIELD, DateFormat.HOUR_OF_DAY1_FIELD,
1044 DateFormat.HOUR_OF_DAY0_FIELD, DateFormat.MINUTE_FIELD,
1045 DateFormat.SECOND_FIELD, DateFormat.MILLISECOND_FIELD,
1046 DateFormat.DAY_OF_WEEK_FIELD, DateFormat.DAY_OF_YEAR_FIELD,
1047 DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD, DateFormat.WEEK_OF_YEAR_FIELD,
1048 DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD,
1049 DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD,
1050 DateFormat.TIMEZONE_FIELD, DateFormat.TIMEZONE_FIELD,
1051 DateFormat.YEAR_FIELD, DateFormat.DAY_OF_WEEK_FIELD,
1052 DateFormat.TIMEZONE_FIELD
1053 };
1054
1055
1056 private static final Field[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID = {
1057 Field.ERA, Field.YEAR, Field.MONTH, Field.DAY_OF_MONTH,
1058 Field.HOUR_OF_DAY1, Field.HOUR_OF_DAY0, Field.MINUTE,
1059 Field.SECOND, Field.MILLISECOND, Field.DAY_OF_WEEK,
1060 Field.DAY_OF_YEAR, Field.DAY_OF_WEEK_IN_MONTH,
1061 Field.WEEK_OF_YEAR, Field.WEEK_OF_MONTH,
1062 Field.AM_PM, Field.HOUR1, Field.HOUR0, Field.TIME_ZONE,
1063 Field.TIME_ZONE,
1064 Field.YEAR, Field.DAY_OF_WEEK,
1065 Field.TIME_ZONE
1066 };
1067
1068
1069
1070
1071 private void subFormat(int patternCharIndex, int count,
1072 FieldDelegate delegate, StringBuffer buffer,
1073 boolean useDateFormatSymbols)
1074 {
1075 int maxIntCount = Integer.MAX_VALUE;
1076 String current = null;
1077 int beginOffset = buffer.length();
1078
1079 int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
1080 int value;
1081 if (field == CalendarBuilder.WEEK_YEAR) {
1082 if (calendar.isWeekDateSupported()) {
1083 value = calendar.getWeekYear();
1084 } else {
1085
1086 patternCharIndex = PATTERN_YEAR;
1087 field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
1088 value = calendar.get(field);
1089 }
1090 } else if (field == CalendarBuilder.ISO_DAY_OF_WEEK) {
1091 value = CalendarBuilder.toISODayOfWeek(calendar.get(Calendar.DAY_OF_WEEK));
1092 } else {
1093 value = calendar.get(field);
1094 }
1095
1096 int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT;
1097 if (!useDateFormatSymbols && field != CalendarBuilder.ISO_DAY_OF_WEEK) {
1098 current = calendar.getDisplayName(field, style, locale);
1099 }
1100
1101
1102
1103
1104
1105 switch (patternCharIndex) {
1106 case PATTERN_ERA:
1107 if (useDateFormatSymbols) {
1108 String[] eras = formatData.getEras();
1109 if (value < eras.length)
1110 current = eras[value];
1111 }
1112 if (current == null)
1113 current = "";
1114 break;
1115
1116 case PATTERN_WEEK_YEAR:
1117 case PATTERN_YEAR:
1118 if (calendar instanceof GregorianCalendar) {
1119 if (count != 2)
1120 zeroPaddingNumber(value, count, maxIntCount, buffer);
1121 else
1122 zeroPaddingNumber(value, 2, 2, buffer);
1123 } else {
1124 if (current == null) {
1125 zeroPaddingNumber(value, style == Calendar.LONG ? 1 : count,
1126 maxIntCount, buffer);
1127 }
1128 }
1129 break;
1130
1131 case PATTERN_MONTH:
1132 if (useDateFormatSymbols) {
1133 String[] months;
1134 if (count >= 4) {
1135 months = formatData.getMonths();
1136 current = months[value];
1137 } else if (count == 3) {
1138 months = formatData.getShortMonths();
1139 current = months[value];
1140 }
1141 } else {
1142 if (count < 3) {
1143 current = null;
1144 }
1145 }
1146 if (current == null) {
1147 zeroPaddingNumber(value+1, count, maxIntCount, buffer);
1148 }
1149 break;
1150
1151 case PATTERN_HOUR_OF_DAY1:
1152 if (current == null) {
1153 if (value == 0)
1154 zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY)+1,
1155 count, maxIntCount, buffer);
1156 else
1157 zeroPaddingNumber(value, count, maxIntCount, buffer);
1158 }
1159 break;
1160
1161 case PATTERN_DAY_OF_WEEK:
1162 if (useDateFormatSymbols) {
1163 String[] weekdays;
1164 if (count >= 4) {
1165 weekdays = formatData.getWeekdays();
1166 current = weekdays[value];
1167 } else {
1168 weekdays = formatData.getShortWeekdays();
1169 current = weekdays[value];
1170 }
1171 }
1172 break;
1173
1174 case PATTERN_AM_PM:
1175 if (useDateFormatSymbols) {
1176 String[] ampm = formatData.getAmPmStrings();
1177 current = ampm[value];
1178 }
1179 break;
1180
1181 case PATTERN_HOUR1:
1182 if (current == null) {
1183 if (value == 0)
1184 zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR)+1,
1185 count, maxIntCount, buffer);
1186 else
1187 zeroPaddingNumber(value, count, maxIntCount, buffer);
1188 }
1189 break;
1190
1191 case PATTERN_ZONE_NAME:
1192 if (current == null) {
1193 if (formatData.locale == null || formatData.isZoneStringsSet) {
1194 int zoneIndex =
1195 formatData.getZoneIndex(calendar.getTimeZone().getID());
1196 if (zoneIndex == -1) {
1197 value = calendar.get(Calendar.ZONE_OFFSET) +
1198 calendar.get(Calendar.DST_OFFSET);
1199 buffer.append(ZoneInfoFile.toCustomID(value));
1200 } else {
1201 int index = (calendar.get(Calendar.DST_OFFSET) == 0) ? 1: 3;
1202 if (count < 4) {
1203
1204 index++;
1205 }
1206 String[][] zoneStrings = formatData.getZoneStringsWrapper();
1207 buffer.append(zoneStrings[zoneIndex][index]);
1208 }
1209 } else {
1210 TimeZone tz = calendar.getTimeZone();
1211 boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0);
1212 int tzstyle = (count < 4 ? TimeZone.SHORT : TimeZone.LONG);
1213 buffer.append(tz.getDisplayName(daylight, tzstyle, formatData.locale));
1214 }
1215 }
1216 break;
1217
1218 case PATTERN_ZONE_VALUE:
1219 value = (calendar.get(Calendar.ZONE_OFFSET) +
1220 calendar.get(Calendar.DST_OFFSET)) / 60000;
1221
1222 int width = 4;
1223 if (value >= 0) {
1224 buffer.append('+');
1225 } else {
1226 width++;
1227 }
1228
1229 int num = (value / 60) * 100 + (value % 60);
1230 CalendarUtils.sprintf0d(buffer, num, width);
1231 break;
1232
1233 case PATTERN_ISO_ZONE:
1234 value = calendar.get(Calendar.ZONE_OFFSET)
1235 + calendar.get(Calendar.DST_OFFSET);
1236
1237 if (value == 0) {
1238 buffer.append('Z');
1239 break;
1240 }
1241
1242 value /= 60000;
1243 if (value >= 0) {
1244 buffer.append('+');
1245 } else {
1246 buffer.append('-');
1247 value = -value;
1248 }
1249
1250 CalendarUtils.sprintf0d(buffer, value / 60, 2);
1251 if (count == 1) {
1252 break;
1253 }
1254
1255 if (count == 3) {
1256 buffer.append(':');
1257 }
1258 CalendarUtils.sprintf0d(buffer, value % 60, 2);
1259 break;
1260
1261 default:
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273 if (current == null) {
1274 zeroPaddingNumber(value, count, maxIntCount, buffer);
1275 }
1276 break;
1277 }
1278
1279 if (current != null) {
1280 buffer.append(current);
1281 }
1282
1283 int fieldID = PATTERN_INDEX_TO_DATE_FORMAT_FIELD[patternCharIndex];
1284 Field f = PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID[patternCharIndex];
1285
1286 delegate.formatted(fieldID, f, f, beginOffset, buffer.length(), buffer);
1287 }
1288
1289
1290
1291
1292 private final void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer)
1293 {
1294
1295
1296
1297
1298 try {
1299 if (zeroDigit == 0) {
1300 zeroDigit = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getZeroDigit();
1301 }
1302 if (value >= 0) {
1303 if (value < 100 && minDigits >= 1 && minDigits <= 2) {
1304 if (value < 10) {
1305 if (minDigits == 2) {
1306 buffer.append(zeroDigit);
1307 }
1308 buffer.append((char)(zeroDigit + value));
1309 } else {
1310 buffer.append((char)(zeroDigit + value / 10));
1311 buffer.append((char)(zeroDigit + value % 10));
1312 }
1313 return;
1314 } else if (value >= 1000 && value < 10000) {
1315 if (minDigits == 4) {
1316 buffer.append((char)(zeroDigit + value / 1000));
1317 value %= 1000;
1318 buffer.append((char)(zeroDigit + value / 100));
1319 value %= 100;
1320 buffer.append((char)(zeroDigit + value / 10));
1321 buffer.append((char)(zeroDigit + value % 10));
1322 return;
1323 }
1324 if (minDigits == 2 && maxDigits == 2) {
1325 zeroPaddingNumber(value % 100, 2, 2, buffer);
1326 return;
1327 }
1328 }
1329 }
1330 } catch (Exception e) {
1331 }
1332
1333 numberFormat.setMinimumIntegerDigits(minDigits);
1334 numberFormat.setMaximumIntegerDigits(maxDigits);
1335 numberFormat.format((long)value, buffer, DontCareFieldPosition.INSTANCE);
1336 }
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374 public Date parse(String text, ParsePosition pos)
1375 {
1376 checkNegativeNumberExpression();
1377
1378 int start = pos.index;
1379 int oldStart = start;
1380 int textLength = text.length();
1381
1382 boolean[] ambiguousYear = {false};
1383
1384 CalendarBuilder calb = new CalendarBuilder();
1385
1386 for (int i = 0; i < compiledPattern.length; ) {
1387 int tag = compiledPattern[i] >>> 8;
1388 int count = compiledPattern[i++] & 0xff;
1389 if (count == 255) {
1390 count = compiledPattern[i++] << 16;
1391 count |= compiledPattern[i++];
1392 }
1393
1394 switch (tag) {
1395 case TAG_QUOTE_ASCII_CHAR:
1396 if (start >= textLength || text.charAt(start) != (char)count) {
1397 pos.index = oldStart;
1398 pos.errorIndex = start;
1399 return null;
1400 }
1401 start++;
1402 break;
1403
1404 case TAG_QUOTE_CHARS:
1405 while (count-- > 0) {
1406 if (start >= textLength || text.charAt(start) != compiledPattern[i++]) {
1407 pos.index = oldStart;
1408 pos.errorIndex = start;
1409 return null;
1410 }
1411 start++;
1412 }
1413 break;
1414
1415 default:
1416
1417
1418
1419
1420
1421 boolean obeyCount = false;
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431 boolean useFollowingMinusSignAsDelimiter = false;
1432
1433 if (i < compiledPattern.length) {
1434 int nextTag = compiledPattern[i] >>> 8;
1435 if (!(nextTag == TAG_QUOTE_ASCII_CHAR ||
1436 nextTag == TAG_QUOTE_CHARS)) {
1437 obeyCount = true;
1438 }
1439
1440 if (hasFollowingMinusSign &&
1441 (nextTag == TAG_QUOTE_ASCII_CHAR ||
1442 nextTag == TAG_QUOTE_CHARS)) {
1443 int c;
1444 if (nextTag == TAG_QUOTE_ASCII_CHAR) {
1445 c = compiledPattern[i] & 0xff;
1446 } else {
1447 c = compiledPattern[i+1];
1448 }
1449
1450 if (c == minusSign) {
1451 useFollowingMinusSignAsDelimiter = true;
1452 }
1453 }
1454 }
1455 start = subParse(text, start, tag, count, obeyCount,
1456 ambiguousYear, pos,
1457 useFollowingMinusSignAsDelimiter, calb);
1458 if (start < 0) {
1459 pos.index = oldStart;
1460 return null;
1461 }
1462 }
1463 }
1464
1465
1466
1467
1468
1469 pos.index = start;
1470
1471 Date parsedDate;
1472 try {
1473 parsedDate = calb.establish(calendar).getTime();
1474
1475
1476 if (ambiguousYear[0]) {
1477 if (parsedDate.before(defaultCenturyStart)) {
1478 parsedDate = calb.addYear(100).establish(calendar).getTime();
1479 }
1480 }
1481 }
1482
1483
1484 catch (IllegalArgumentException e) {
1485 pos.errorIndex = start;
1486 pos.index = oldStart;
1487 return null;
1488 }
1489
1490 return parsedDate;
1491 }
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502 private int matchString(String text, int start, int field, String[] data, CalendarBuilder calb)
1503 {
1504 int i = 0;
1505 int count = data.length;
1506
1507 if (field == Calendar.DAY_OF_WEEK) i = 1;
1508
1509
1510
1511
1512
1513 int bestMatchLength = 0, bestMatch = -1;
1514 for (; i<count; ++i)
1515 {
1516 int length = data[i].length();
1517
1518
1519 if (length > bestMatchLength &&
1520 text.regionMatches(true, start, data[i], 0, length))
1521 {
1522 bestMatch = i;
1523 bestMatchLength = length;
1524 }
1525 }
1526 if (bestMatch >= 0)
1527 {
1528 calb.set(field, bestMatch);
1529 return start + bestMatchLength;
1530 }
1531 return -start;
1532 }
1533
1534
1535
1536
1537
1538
1539 private int matchString(String text, int start, int field,
1540 Map<String,Integer> data, CalendarBuilder calb) {
1541 if (data != null) {
1542 String bestMatch = null;
1543
1544 for (String name : data.keySet()) {
1545 int length = name.length();
1546 if (bestMatch == null || length > bestMatch.length()) {
1547 if (text.regionMatches(true, start, name, 0, length)) {
1548 bestMatch = name;
1549 }
1550 }
1551 }
1552
1553 if (bestMatch != null) {
1554 calb.set(field, data.get(bestMatch));
1555 return start + bestMatch.length();
1556 }
1557 }
1558 return -start;
1559 }
1560
1561 private int matchZoneString(String text, int start, String[] zoneNames) {
1562 for (int i = 1; i <= 4; ++i) {
1563
1564
1565 String zoneName = zoneNames[i];
1566 if (text.regionMatches(true, start,
1567 zoneName, 0, zoneName.length())) {
1568 return i;
1569 }
1570 }
1571 return -1;
1572 }
1573
1574 private boolean matchDSTString(String text, int start, int zoneIndex, int standardIndex,
1575 String[][] zoneStrings) {
1576 int index = standardIndex + 2;
1577 String zoneName = zoneStrings[zoneIndex][index];
1578 if (text.regionMatches(true, start,
1579 zoneName, 0, zoneName.length())) {
1580 return true;
1581 }
1582 return false;
1583 }
1584
1585
1586
1587
1588
1589 private int subParseZoneString(String text, int start, CalendarBuilder calb) {
1590 boolean useSameName = false;
1591 TimeZone currentTimeZone = getTimeZone();
1592
1593
1594
1595
1596 int zoneIndex = formatData.getZoneIndex(currentTimeZone.getID());
1597 TimeZone tz = null;
1598 String[][] zoneStrings = formatData.getZoneStringsWrapper();
1599 String[] zoneNames = null;
1600 int nameIndex = 0;
1601 if (zoneIndex != -1) {
1602 zoneNames = zoneStrings[zoneIndex];
1603 if ((nameIndex = matchZoneString(text, start, zoneNames)) > 0) {
1604 if (nameIndex <= 2) {
1605
1606 useSameName = zoneNames[nameIndex].equalsIgnoreCase(zoneNames[nameIndex + 2]);
1607 }
1608 tz = TimeZone.getTimeZone(zoneNames[0]);
1609 }
1610 }
1611 if (tz == null) {
1612 zoneIndex = formatData.getZoneIndex(TimeZone.getDefault().getID());
1613 if (zoneIndex != -1) {
1614 zoneNames = zoneStrings[zoneIndex];
1615 if ((nameIndex = matchZoneString(text, start, zoneNames)) > 0) {
1616 if (nameIndex <= 2) {
1617 useSameName = zoneNames[nameIndex].equalsIgnoreCase(zoneNames[nameIndex + 2]);
1618 }
1619 tz = TimeZone.getTimeZone(zoneNames[0]);
1620 }
1621 }
1622 }
1623
1624 if (tz == null) {
1625 int len = zoneStrings.length;
1626 for (int i = 0; i < len; i++) {
1627 zoneNames = zoneStrings[i];
1628 if ((nameIndex = matchZoneString(text, start, zoneNames)) > 0) {
1629 if (nameIndex <= 2) {
1630 useSameName = zoneNames[nameIndex].equalsIgnoreCase(zoneNames[nameIndex + 2]);
1631 }
1632 tz = TimeZone.getTimeZone(zoneNames[0]);
1633 break;
1634 }
1635 }
1636 }
1637 if (tz != null) {
1638 if (!tz.equals(currentTimeZone)) {
1639 setTimeZone(tz);
1640 }
1641
1642
1643
1644
1645
1646
1647 int dstAmount = (nameIndex >= 3) ? tz.getDSTSavings() : 0;
1648 if (!(useSameName || (nameIndex >= 3 && dstAmount == 0))) {
1649 calb.set(Calendar.ZONE_OFFSET, tz.getRawOffset())
1650 .set(Calendar.DST_OFFSET, dstAmount);
1651 }
1652 return (start + zoneNames[nameIndex].length());
1653 }
1654 return 0;
1655 }
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669 private int subParseNumericZone(String text, int start, int sign, int count,
1670 boolean colon, CalendarBuilder calb) {
1671 int index = start;
1672
1673 parse:
1674 try {
1675 char c = text.charAt(index++);
1676
1677 int hours;
1678 if (!isDigit(c)) {
1679 break parse;
1680 }
1681 hours = c - '0';
1682 c = text.charAt(index++);
1683 if (isDigit(c)) {
1684 hours = hours * 10 + (c - '0');
1685 } else {
1686
1687
1688 if (count > 0 || !colon) {
1689 break parse;
1690 }
1691 --index;
1692 }
1693 if (hours > 23) {
1694 break parse;
1695 }
1696 int minutes = 0;
1697 if (count != 1) {
1698
1699 c = text.charAt(index++);
1700 if (colon) {
1701 if (c != ':') {
1702 break parse;
1703 }
1704 c = text.charAt(index++);
1705 }
1706 if (!isDigit(c)) {
1707 break parse;
1708 }
1709 minutes = c - '0';
1710 c = text.charAt(index++);
1711 if (!isDigit(c)) {
1712 break parse;
1713 }
1714 minutes = minutes * 10 + (c - '0');
1715 if (minutes > 59) {
1716 break parse;
1717 }
1718 }
1719 minutes += hours * 60;
1720 calb.set(Calendar.ZONE_OFFSET, minutes * MILLIS_PER_MINUTE * sign)
1721 .set(Calendar.DST_OFFSET, 0);
1722 return index;
1723 } catch (IndexOutOfBoundsException e) {
1724 }
1725 return 1 - index;
1726 }
1727
1728 private boolean isDigit(char c) {
1729 return c >= '0' && c <= '9';
1730 }
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749 private int subParse(String text, int start, int patternCharIndex, int count,
1750 boolean obeyCount, boolean[] ambiguousYear,
1751 ParsePosition origPos,
1752 boolean useFollowingMinusSignAsDelimiter, CalendarBuilder calb) {
1753 Number number = null;
1754 int value = 0;
1755 ParsePosition pos = new ParsePosition(0);
1756 pos.index = start;
1757 if (patternCharIndex == PATTERN_WEEK_YEAR && !calendar.isWeekDateSupported()) {
1758
1759 patternCharIndex = PATTERN_YEAR;
1760 }
1761 int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
1762
1763
1764
1765 for (;;) {
1766 if (pos.index >= text.length()) {
1767 origPos.errorIndex = start;
1768 return -1;
1769 }
1770 char c = text.charAt(pos.index);
1771 if (c != ' ' && c != '\t') break;
1772 ++pos.index;
1773 }
1774
1775 parsing:
1776 {
1777
1778
1779
1780
1781 if (patternCharIndex == PATTERN_HOUR_OF_DAY1 ||
1782 patternCharIndex == PATTERN_HOUR1 ||
1783 (patternCharIndex == PATTERN_MONTH && count <= 2) ||
1784 patternCharIndex == PATTERN_YEAR ||
1785 patternCharIndex == PATTERN_WEEK_YEAR) {
1786
1787
1788 if (obeyCount) {
1789 if ((start+count) > text.length()) {
1790 break parsing;
1791 }
1792 number = numberFormat.parse(text.substring(0, start+count), pos);
1793 } else {
1794 number = numberFormat.parse(text, pos);
1795 }
1796 if (number == null) {
1797 if (patternCharIndex != PATTERN_YEAR || calendar instanceof GregorianCalendar) {
1798 break parsing;
1799 }
1800 } else {
1801 value = number.intValue();
1802
1803 if (useFollowingMinusSignAsDelimiter && (value < 0) &&
1804 (((pos.index < text.length()) &&
1805 (text.charAt(pos.index) != minusSign)) ||
1806 ((pos.index == text.length()) &&
1807 (text.charAt(pos.index-1) == minusSign)))) {
1808 value = -value;
1809 pos.index--;
1810 }
1811 }
1812 }
1813
1814 boolean useDateFormatSymbols = useDateFormatSymbols();
1815
1816 int index;
1817 switch (patternCharIndex) {
1818 case PATTERN_ERA:
1819 if (useDateFormatSymbols) {
1820 if ((index = matchString(text, start, Calendar.ERA, formatData.getEras(), calb)) > 0) {
1821 return index;
1822 }
1823 } else {
1824 Map<String, Integer> map = calendar.getDisplayNames(field,
1825 Calendar.ALL_STYLES,
1826 locale);
1827 if ((index = matchString(text, start, field, map, calb)) > 0) {
1828 return index;
1829 }
1830 }
1831 break parsing;
1832
1833 case PATTERN_WEEK_YEAR:
1834 case PATTERN_YEAR:
1835 if (!(calendar instanceof GregorianCalendar)) {
1836
1837
1838 int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT;
1839 Map<String, Integer> map = calendar.getDisplayNames(field, style, locale);
1840 if (map != null) {
1841 if ((index = matchString(text, start, field, map, calb)) > 0) {
1842 return index;
1843 }
1844 }
1845 calb.set(field, value);
1846 return pos.index;
1847 }
1848
1849
1850
1851
1852
1853
1854
1855 if (count <= 2 && (pos.index - start) == 2
1856 && Character.isDigit(text.charAt(start))
1857 && Character.isDigit(text.charAt(start+1))) {
1858
1859
1860
1861
1862
1863
1864
1865
1866 int ambiguousTwoDigitYear = defaultCenturyStartYear % 100;
1867 ambiguousYear[0] = value == ambiguousTwoDigitYear;
1868 value += (defaultCenturyStartYear/100)*100 +
1869 (value < ambiguousTwoDigitYear ? 100 : 0);
1870 }
1871 calb.set(field, value);
1872 return pos.index;
1873
1874 case PATTERN_MONTH:
1875 if (count <= 2)
1876 {
1877
1878
1879
1880 calb.set(Calendar.MONTH, value - 1);
1881 return pos.index;
1882 }
1883
1884 if (useDateFormatSymbols) {
1885
1886
1887
1888 int newStart = 0;
1889 if ((newStart = matchString(text, start, Calendar.MONTH,
1890 formatData.getMonths(), calb)) > 0) {
1891 return newStart;
1892 }
1893
1894 if ((index = matchString(text, start, Calendar.MONTH,
1895 formatData.getShortMonths(), calb)) > 0) {
1896 return index;
1897 }
1898 } else {
1899 Map<String, Integer> map = calendar.getDisplayNames(field,
1900 Calendar.ALL_STYLES,
1901 locale);
1902 if ((index = matchString(text, start, field, map, calb)) > 0) {
1903 return index;
1904 }
1905 }
1906 break parsing;
1907
1908 case PATTERN_HOUR_OF_DAY1:
1909 if (!isLenient()) {
1910
1911 if (value < 1 || value > 24) {
1912 break parsing;
1913 }
1914 }
1915
1916 if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY)+1)
1917 value = 0;
1918 calb.set(Calendar.HOUR_OF_DAY, value);
1919 return pos.index;
1920
1921 case PATTERN_DAY_OF_WEEK:
1922 {
1923 if (useDateFormatSymbols) {
1924
1925
1926 int newStart = 0;
1927 if ((newStart=matchString(text, start, Calendar.DAY_OF_WEEK,
1928 formatData.getWeekdays(), calb)) > 0) {
1929 return newStart;
1930 }
1931
1932 if ((index = matchString(text, start, Calendar.DAY_OF_WEEK,
1933 formatData.getShortWeekdays(), calb)) > 0) {
1934 return index;
1935 }
1936 } else {
1937 int[] styles = { Calendar.LONG, Calendar.SHORT };
1938 for (int style : styles) {
1939 Map<String,Integer> map = calendar.getDisplayNames(field, style, locale);
1940 if ((index = matchString(text, start, field, map, calb)) > 0) {
1941 return index;
1942 }
1943 }
1944 }
1945 }
1946 break parsing;
1947
1948 case PATTERN_AM_PM:
1949 if (useDateFormatSymbols) {
1950 if ((index = matchString(text, start, Calendar.AM_PM,
1951 formatData.getAmPmStrings(), calb)) > 0) {
1952 return index;
1953 }
1954 } else {
1955 Map<String,Integer> map = calendar.getDisplayNames(field, Calendar.ALL_STYLES, locale);
1956 if ((index = matchString(text, start, field, map, calb)) > 0) {
1957 return index;
1958 }
1959 }
1960 break parsing;
1961
1962 case PATTERN_HOUR1:
1963 if (!isLenient()) {
1964
1965 if (value < 1 || value > 12) {
1966 break parsing;
1967 }
1968 }
1969
1970 if (value == calendar.getLeastMaximum(Calendar.HOUR)+1)
1971 value = 0;
1972 calb.set(Calendar.HOUR, value);
1973 return pos.index;
1974
1975 case PATTERN_ZONE_NAME:
1976 case PATTERN_ZONE_VALUE:
1977 {
1978 int sign = 0;
1979 try {
1980 char c = text.charAt(pos.index);
1981 if (c == '+') {
1982 sign = 1;
1983 } else if (c == '-') {
1984 sign = -1;
1985 }
1986 if (sign == 0) {
1987
1988 if ((c == 'G' || c == 'g')
1989 && (text.length() - start) >= GMT.length()
1990 && text.regionMatches(true, start, GMT, 0, GMT.length())) {
1991 pos.index = start + GMT.length();
1992
1993 if ((text.length() - pos.index) > 0) {
1994 c = text.charAt(pos.index);
1995 if (c == '+') {
1996 sign = 1;
1997 } else if (c == '-') {
1998 sign = -1;
1999 }
2000 }
2001
2002 if (sign == 0) {
2003 calb.set(Calendar.ZONE_OFFSET, 0)
2004 .set(Calendar.DST_OFFSET, 0);
2005 return pos.index;
2006 }
2007
2008
2009 int i = subParseNumericZone(text, ++pos.index,
2010 sign, 0, true, calb);
2011 if (i > 0) {
2012 return i;
2013 }
2014 pos.index = -i;
2015 } else {
2016
2017
2018 int i = subParseZoneString(text, pos.index, calb);
2019 if (i > 0) {
2020 return i;
2021 }
2022 pos.index = -i;
2023 }
2024 } else {
2025
2026 int i = subParseNumericZone(text, ++pos.index,
2027 sign, 0, false, calb);
2028 if (i > 0) {
2029 return i;
2030 }
2031 pos.index = -i;
2032 }
2033 } catch (IndexOutOfBoundsException e) {
2034 }
2035 }
2036 break parsing;
2037
2038 case PATTERN_ISO_ZONE:
2039 {
2040 if ((text.length() - pos.index) <= 0) {
2041 break parsing;
2042 }
2043
2044 int sign = 0;
2045 char c = text.charAt(pos.index);
2046 if (c == 'Z') {
2047 calb.set(Calendar.ZONE_OFFSET, 0).set(Calendar.DST_OFFSET, 0);
2048 return ++pos.index;
2049 }
2050
2051
2052 if (c == '+') {
2053 sign = 1;
2054 } else if (c == '-') {
2055 sign = -1;
2056 } else {
2057 ++pos.index;
2058 break parsing;
2059 }
2060 int i = subParseNumericZone(text, ++pos.index, sign, count,
2061 count == 3, calb);
2062 if (i > 0) {
2063 return i;
2064 }
2065 pos.index = -i;
2066 }
2067 break parsing;
2068
2069 default:
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083 if (obeyCount) {
2084 if ((start+count) > text.length()) {
2085 break parsing;
2086 }
2087 number = numberFormat.parse(text.substring(0, start+count), pos);
2088 } else {
2089 number = numberFormat.parse(text, pos);
2090 }
2091 if (number != null) {
2092 value = number.intValue();
2093
2094 if (useFollowingMinusSignAsDelimiter && (value < 0) &&
2095 (((pos.index < text.length()) &&
2096 (text.charAt(pos.index) != minusSign)) ||
2097 ((pos.index == text.length()) &&
2098 (text.charAt(pos.index-1) == minusSign)))) {
2099 value = -value;
2100 pos.index--;
2101 }
2102
2103 calb.set(field, value);
2104 return pos.index;
2105 }
2106 break parsing;
2107 }
2108 }
2109
2110
2111 origPos.errorIndex = pos.index;
2112 return -1;
2113 }
2114
2115 private final String getCalendarName() {
2116 return calendar.getClass().getName();
2117 }
2118
2119 private boolean useDateFormatSymbols() {
2120 if (useDateFormatSymbols) {
2121 return true;
2122 }
2123 return isGregorianCalendar() || locale == null;
2124 }
2125
2126 private boolean isGregorianCalendar() {
2127 return "java.util.GregorianCalendar".equals(getCalendarName());
2128 }
2129
2130
2131
2132
2133
2134
2135
2136 private String translatePattern(String pattern, String from, String to) {
2137 StringBuilder result = new StringBuilder();
2138 boolean inQuote = false;
2139 for (int i = 0; i < pattern.length(); ++i) {
2140 char c = pattern.charAt(i);
2141 if (inQuote) {
2142 if (c == '\'')
2143 inQuote = false;
2144 }
2145 else {
2146 if (c == '\'')
2147 inQuote = true;
2148 else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2149 int ci = from.indexOf(c);
2150 if (ci >= 0) {
2151
2152
2153
2154 if (ci < to.length()) {
2155 c = to.charAt(ci);
2156 }
2157 } else {
2158 throw new IllegalArgumentException("Illegal pattern " +
2159 " character '" +
2160 c + "'");
2161 }
2162 }
2163 }
2164 result.append(c);
2165 }
2166 if (inQuote)
2167 throw new IllegalArgumentException("Unfinished quote in pattern");
2168 return result.toString();
2169 }
2170
2171
2172
2173
2174
2175
2176 public String toPattern() {
2177 return pattern;
2178 }
2179
2180
2181
2182
2183
2184
2185 public String toLocalizedPattern() {
2186 return translatePattern(pattern,
2187 DateFormatSymbols.patternChars,
2188 formatData.getLocalPatternChars());
2189 }
2190
2191
2192
2193
2194
2195
2196
2197
2198 public void applyPattern(String pattern)
2199 {
2200 compiledPattern = compile(pattern);
2201 this.pattern = pattern;
2202 }
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212 public void applyLocalizedPattern(String pattern) {
2213 String p = translatePattern(pattern,
2214 formatData.getLocalPatternChars(),
2215 DateFormatSymbols.patternChars);
2216 compiledPattern = compile(p);
2217 this.pattern = p;
2218 }
2219
2220
2221
2222
2223
2224
2225
2226 public DateFormatSymbols getDateFormatSymbols()
2227 {
2228 return (DateFormatSymbols)formatData.clone();
2229 }
2230
2231
2232
2233
2234
2235
2236
2237
2238 public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols)
2239 {
2240 this.formatData = (DateFormatSymbols)newFormatSymbols.clone();
2241 useDateFormatSymbols = true;
2242 }
2243
2244
2245
2246
2247
2248
2249
2250 public Object clone() {
2251 SimpleDateFormat other = (SimpleDateFormat) super.clone();
2252 other.formatData = (DateFormatSymbols) formatData.clone();
2253 return other;
2254 }
2255
2256
2257
2258
2259
2260
2261 public int hashCode()
2262 {
2263 return pattern.hashCode();
2264
2265 }
2266
2267
2268
2269
2270
2271
2272
2273
2274 public boolean equals(Object obj)
2275 {
2276 if (!super.equals(obj)) return false;
2277 SimpleDateFormat that = (SimpleDateFormat) obj;
2278 return (pattern.equals(that.pattern)
2279 && formatData.equals(that.formatData));
2280 }
2281
2282
2283
2284
2285
2286
2287
2288 private void readObject(ObjectInputStream stream)
2289 throws IOException, ClassNotFoundException {
2290 stream.defaultReadObject();
2291
2292 try {
2293 compiledPattern = compile(pattern);
2294 } catch (Exception e) {
2295 throw new InvalidObjectException("invalid pattern");
2296 }
2297
2298 if (serialVersionOnStream < 1) {
2299
2300 initializeDefaultCentury();
2301 }
2302 else {
2303
2304 parseAmbiguousDatesAsAfter(defaultCenturyStart);
2305 }
2306 serialVersionOnStream = currentSerialVersion;
2307
2308
2309
2310
2311
2312 TimeZone tz = getTimeZone();
2313 if (tz instanceof SimpleTimeZone) {
2314 String id = tz.getID();
2315 TimeZone zi = TimeZone.getTimeZone(id);
2316 if (zi != null && zi.hasSameRules(tz) && zi.getID().equals(id)) {
2317 setTimeZone(zi);
2318 }
2319 }
2320 }
2321
2322
2323
2324
2325
2326 private void checkNegativeNumberExpression() {
2327 if ((numberFormat instanceof DecimalFormat) &&
2328 !numberFormat.equals(originalNumberFormat)) {
2329 String numberPattern = ((DecimalFormat)numberFormat).toPattern();
2330 if (!numberPattern.equals(originalNumberPattern)) {
2331 hasFollowingMinusSign = false;
2332
2333 int separatorIndex = numberPattern.indexOf(';');
2334
2335
2336 if (separatorIndex > -1) {
2337 int minusIndex = numberPattern.indexOf('-', separatorIndex);
2338 if ((minusIndex > numberPattern.lastIndexOf('0')) &&
2339 (minusIndex > numberPattern.lastIndexOf('#'))) {
2340 hasFollowingMinusSign = true;
2341 minusSign = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getMinusSign();
2342 }
2343 }
2344 originalNumberPattern = numberPattern;
2345 }
2346 originalNumberFormat = numberFormat;
2347 }
2348 }
2349
2350 }